import * as types from './mutation-types'
import * as constant from '../../common/constant'
import { transform, untransform } from '../../common/uri'
import * as utils from '../../common/utils'
import Vue from 'vue'
import axios from 'axios'
import route from '@/router/'
import {m, notify} from "@/store/yg/common/local"
import { stat } from 'fs';
import { Message } from 'element-ui'

axios.defaults.withCredentials=true;

function resetModalLink(state) {
  if (state.entry.caption) {
    state.modalLink.caption = state.entry.caption;
  }
  if (state.entry.icon) {
    state.modalLink.icon = state.entry.icon;
  }
}

function dotKeyIntoHash(dotKey, value) {
	var keys = dotKey.split(".").reverse();
	var object = value;
	for (var i = 0; i < (keys.length - 1); ++i) {
		var object = keyIntoHash(keys[i], value);
		value = object;
	}
	return object;
}

function keyIntoHash(key, value) {
	var object = {};
	object[key] = value;

	return object;
}

function contains(row, field, obj) {
  if ( ! obj) {
    return false;
  }
  var keys = field.split(".");
  var values = row[keys[0]];
  if ( ! values) {
    return false;
  }
  var i = values.length;
  while (i--) {
    var value = values[i];
    if (keys[1]) {
      value = value[keys[1]];
    }
    if (value === obj) {
      return true;
    }
  }
  return false;
}

function fetchValues(object, field, options) {
  var values = [];
  if (field.indexOf(".") < 0) {
    if (object[field]) {
      for (var k in object[field]) {
        values.push(object[field][k] + '');
      }
      return values;
    }

    return values;
  }
  var keys = field.split(".");
  var vs = object[keys[0]];

  var i = vs.length;
  while (i--) {
    var value = vs[i];
    if (keys[1]) {
      value = value[keys[1]];
    }
    for (var k in options) {
      if (k == value) {
        values.push(k);
      }
    }
  }

  return values;
}

function fullfilSources(state) {
  var object = state.row;
  if ( ! state.entry.panel || ! object) {
    return;
  }

  for (var i = 0; i < state.entry.panel.content.length; ++i) {
    var rows = state.entry.panel.content[i].column;
    for (var k = 0; k < rows.length; ++k) {
      var row = rows[k];
      var source = row.source;
      var field = row.field;
      var type = row.type;
      if (row.arg && state.row[row.arg]) {
        row.source = source + state.row[row.arg];
      }

      if (type == 'tree') {
        var options = state.entry.sources[source];
        for (var oi = 0; oi < options.length; ++oi) {
          if (options[oi].children.length > 0) {
            options[oi].label = m(options[oi].label);
            for (var oj = 0; oj < options[oi].children.length; ++oj) {
              var firstChild = options[oi].children[oj];
              firstChild.label = m(firstChild.label);
              if (firstChild.children.length > 0) {
                for (var ok = 0; ok < firstChild.children.length; ++ok) {
                  var secondChild = firstChild.children[ok];
                  secondChild.label = m(secondChild.label);
                  if (contains(object, field, secondChild.id)) {
                    secondChild.checked = true;
                  } else {
                    secondChild.checked = false;
                  }
                }
              } else if (contains(object, field, firstChild.id)) {
                firstChild.checked = true;
              } else {
                firstChild.checked = false;
              }
            }
          } else if (contains(object, field, options[oi].id)) {
            options[oi].checked = true;
          } else {
            options[oi].checked = false;
          }
        }
      } else if (type == 'multiselect' || type == 'checkbox') {
        var options = state.entry.sources[source];
        var values = fetchValues(object, field, options);
        Vue.set(object, field, values);
      } else if (type == 'matrix') {
        if (object[field] && object[field].types) {
          for (let i = 0; i < object[field].types.length; ++i) {
            let type = object[field].types[i];
            if (type.source) {
              loadEditOptionSource(state, type.source);
            }
          }
        }
      }
    }
  }
}

function fullfilEditRow(state) {
  if ( ! state.entry.panel) {
    return;
  }

  for (var i = 0; i < state.entry.panel.content.length; ++i) {
    var content = state.entry.panel.content[i];
    var rows = content.column2 ? content.column.concat(content.column2) : content.column;

    for (var k = 0; k < rows.length; ++k) {
      var row = rows[k];

      if (row.field && row.field.indexOf(".") > 0) {
        var ks = row.field.split(".");
        if (state.row[ks[0]]) {
          Vue.set(state.row, row.field, state.row[ks[0]][ks[1]]);
        }
      }

      if (row.type == 'matrix' && null != state.row[row.field]) {
        let matrix = state.row[row.field]['data'];
        for (let i = 0; i < matrix.length; ++i) {
          for (let j = 0; j < matrix[i].length; ++j) {
            state.row[row.field + '-' + i + '-' + j] = matrix[i][j];
          }
        }
      }

      if (undefined !== state.row[row.field] && null !== state.row[row.field]) {
        continue;
      }


      if (row.field) {
        if (row.type == 'text' ||
          row.type == 'select' ||
          row.type == 'editor') {
          Vue.set(state.row, row.field, '');
        } else if (row.type == 'multiselect' ||
          row.type == 'checkbox' ||
          row.type == 'cascader' ||
          row.type == 'file'
          ) {
          Vue.set(state.row, row.field, []);
        }
      }
    }
  }
}

function stringifyEditField(state) {
  if ( ! state.entry.panel) {
    return;
  }
  for (var i = 0; i < state.entry.panel.content.length; ++i) {
    var rows = state.entry.panel.content[i].column;
    for (var k = 0; k < rows.length; ++k) {
      var row = rows[k];
      if (state.row[row.field]) {
        if (row.type == 'select' ||
          row.type == 'radio') {
          //Vue.set(state.row, row.field, state.row[row.field]);
        }
      } else {
        if (row.type == 'multiselect' || row.type == 'cascader') {
          Vue.set(state.row, row.field, []);
        }
      }
    }
  }
}


function initSources(state) {
  if ( ! state.entry.sources) {
    state.entry.sources = {};
  }
  for (let i in state.entry.panel.content) {
    let con = state.entry.panel.content[i];
    for (let j in con.column) {
      let col = con.column[j];
      if (col.source && ! state.entry.sources[col.source]) {
        state.entry.sources[col.source] = {};
      }
    }
    if (con.column2) {
      for (let j in con.column2) {
        let col = con.column2[j];
        if (col.source && ! state.entry.sources[col.source]) {
          state.entry.sources[col.source] = {};
        }
      }
    }
  }
}

function fullfilEditRowSourcePost(state, bean, arg) {
  if (arg) {
    var uri = "/api/entry/source?bean=" + bean + "&arg=" + arg;

    bean = bean + arg;
    axios.post(constant.API_SERVER + uri).then(response => {
        Vue.set(state.entry.sources, bean, response.data);
      }, error => {
        console.log(error);
      }
    );
  }
}

function fullfilEditRowSource(state) {
    for (var k in state.entry.sources) {
      var bean = k;
      var arg = "";
      if('string' == typeof state.entry.sources[k]) {
        bean = k.split(" ")[0];
        arg = k.split(" ")[1]
      }
      fullfilEditRowSourcePost(state, bean, state.row[arg]);
    }
}

function fullfilEditRowColumnDynamic(state, content, i) {
  var dynamicIndex = -1;
  var dynamicRows = [];
  var dynamicRows2 = [];
  var column = content.column;
  for (var j = 0; j < column.length; j++) {
    var row = column[j];
    if (row.dynamic) {
      var data = state.row[row.dynamic];
      if (data) {
        dynamicIndex = j;
        var protos = JSON.parse(data);
        for (var a = 0; a < protos.length; a++) {
          var proto = protos[a];
          var one = {};
          if (!proto.name) {
            continue;
          }
          one.caption = proto.name;
          one.field = "__dynamic__." + row.dynamic + "." + proto.name;
          one.type = "text";
          if (proto.type) {
            one.type = proto.type;
          }
          if (proto.value) {
            state.row[one.field] = proto.value;
          }
          if (proto.type == 'caption') {
            one.class = "large blue"
          }
          var validator = [];
          if (proto.restrict) {
            for (var r = 0; r < proto.restrict.length; r++) {
              var restrict = proto.restrict[r];
              if ("required" == restrict) {
                validator.push({ required: true, message: m('required', true) });
              } else if ("gt_zero" == restrict) {
                validator.push({ type: 'number', min: 1, message: m('greater_than_zero', true) });
                one['field-type'] = 'number';
              } else if ("integer" == restrict) {
                validator.push({ type: 'integer', message: m('must_integer', true) });
              }
            }
          }
          one.validator = validator;
          one.prop = 'prop_' + i + '_' + a;

          if (proto.datasource) {
            one.type = "select";
            one.source = "dataSourceSource" + proto.datasource;
            fullfilEditRowSourcePost(state, "dataSourceSource", proto.datasource);
          }
          var r = JSON.parse(JSON.stringify(one));
          if (proto.column2) {
            dynamicRows2.push(r);
          } else {
            dynamicRows.push(r);
          }
        }
      }
    }
  }
  if (dynamicIndex >= 0) {
    var newRows = [];
    newRows = column.slice(0, dynamicIndex)
    newRows = newRows.concat(dynamicRows);
    if (dynamicIndex + 1 < column.length) {
      newRows = newRows.concat(column.slice(dynamicIndex + 1));
    }

    content.column = newRows;
    content.column2 = content.column2 ? content.column2.concat(dynamicRows2) : dynamicRows2;
  }
}

function fullfilEditRowDynamic(state) {
  if ( ! state.entry.panel) {
    return;
  }
  for (var i = 0; i < state.entry.panel.content.length; i++) {
    var content = state.entry.panel.content[i];
    // dynamic配置只能放在第一列, 但是解析的数据可能会以追加的形式展示在第二列
    fullfilEditRowColumnDynamic(state, content, i);
  }
}

function commitEditAction(state, uri, mainUri, callafter) {
  state.response = {};
  axios.post(constant.API_SERVER + uri).then(response => {
      state.response = response.data;
      if (callafter) {
        (callafter)(state.response);
      }

      // 清除entry缓存
      utils.evictEntry(state.entry)
    }, response => {
      // error callback
      Vue.set(state.response, 'type', 'danger');
      Vue.set(state.response, 'msg', response.response.data.error);
      state.commitAction = '';

      if (callafter) {
        (callafter)(state.response);
      }
    }
  );
}

function parseDynamicField(field, value, dynamics, state) {
  if (undefined ===  field) {
    return false;
  }

  if (field.indexOf("__dynamic__.") == 0) {
    field = field.replace("__dynamic__.", "");
    var dynamicField = field.split(".")[0];
    var innerField = field.split(".")[1];
    if ( ! dynamics[dynamicField]) {
      dynamics[dynamicField] = {};
    }
    dynamics[dynamicField][innerField] = value;

    return true;
  }
  return false;
}

function fillRowValues(state, row, data, dynamics, ignoreId) {
  var source = row.source;
  var field = row.field;
  var type = row.type;

  if (row.convert) {
    data[field + '__convert'] = row.convert;
  }

  if (type == 'multiselect') {
    var values = [];
    var options = state.row[field];
    if (options) {
      for (var oi = 0; oi < options.length; ++oi) {
        var v = options[oi];
        if (options[oi].value) {
          v = options[oi].value;
        }
        var object = dotKeyIntoHash(field, v);
        values.push(object);
      }
    }
    if ( ! parseDynamicField(field, values, dynamics, state)) {
      data[field.split(".")[0]] = values;
    }
  } else if (type == 'ignore') {
    if (field == 'id') {
      ignoreId = true;
    }
  } else if (type == 'range' || type == 'range-date' || type == 'range-datetime' || type == 'range-time') {
    var start = field + '__start';
    var end = field + '__end';
    if (undefined !== state.row[start]) {
      data[start] = utils.trim(state.row[start]);
    }
    if (undefined !== state.row[end]) {
      data[end] = utils.trim(state.row[end]);
    }
  } else if (type == 'file') {
    var urls = [];
    for (var i in state.row[field]) {
      urls.push(state.row[field][i].fileName);
    }
    data[field + 'Metas'] = state.row[field];
    data[field] = urls;
  } else if (type == 'matrix') {
    let matrix = [];
    for (let i = 0; i < state.row[field].starts.length; ++i) {
      let innerMatrix = [];
      for (let j = 0; j < (state.row[field].heads.length - 1); ++j) {
        innerMatrix.push(state.row[field + '-' + i + '-' + j]);
      }
      matrix.push(innerMatrix);
    }
    data[field] = {data: matrix, starts: state.row[field].starts, heads: state.row[field].heads, types: state.row[field].types};
  } else if (field) {
    var value = state.row[field];

    if(undefined !== value && value) {
        if(typeof value == "string"){
            value = utils.trim(value);
        }
        else if(typeof value == "object" &&  value.hasOwnProperty('value')){
            value = utils.trim(value['value']);
        }
    }

    if ( ! parseDynamicField(field, value, dynamics, state)) {
      data[field] = value;
    }
  }

  return ignoreId;
}

function rowDotToObject(data) {
  var objects = {};
  for (var key in data) {
    if (key.indexOf(".") > 0) {
      var ks = key.split(".");
      if ( ! objects[ks[0]]) {
        objects[ks[0]] = {};
      }
      objects[ks[0]][ks[1]] = data[key];
      delete data[key];
    }
  }
  Object.assign(data, objects);
}


function loadEntry(state, link) {
  var entryUri = link;
  var processUri = '';
  var mainUri = '';
  if (typeof link === 'object') {
    entryUri = link.entryUri ? link.entryUri : link.uri;
    processUri = link.processUri;
    mainUri = link.mainUri;
  }
  var ouri = entryUri;
  state.response = {};
  state.entry = {};
  state.dataChanged = false;
  state.editWarningMessage = null;
  entryUri = untransform(entryUri);

  if (link.ids) {
    state.ids = link.ids;
  }

  /*
    优先加载JS配置，如果无法加载则获取YAML配置
  */
  var e = null;
  try {
    e = require('@/layout/edit' + entryUri + '/Entry.js');
  } catch(ex) {
  };
  if (e) {
    state.entry = e;
    state.entry.commit = {client: state.entry.client};
    state.modal = true;
    initSources(state);
    fullfilEditRowSource(state);
    return new Promise((resolve, reject) => {
      resolve(state.entry);
    });
  }

  if (localStorage['edit_' + ouri] && !constant.DEV_MODE) {
    return new Promise((resolve, reject) => {
      state.entry = JSON.parse(localStorage['edit_' + ouri]);
      resetModalLink(state);
      resolve(state.entry);
    });
  }
  var uri = "/api/entry/edit?entryUri=" + entryUri;
  if (mainUri) {
    uri += "&mainUri=" + mainUri;
  }
  return new Promise((resolve, reject) => {
    axios.post(constant.API_SERVER + uri).then(response => {
      state.entry = response.data;

      if (processUri) {
        state.entry.commit.uri = processUri;
      }

      localStorage['edit_' + ouri] = JSON.stringify(state.entry);
      utils.storeSource(state.entry.sources, 'edit_' + ouri);
      resetModalLink(state);
      fullfilEditRowDynamic(state);
      fullfilSources(state);
      if (Object.keys(state.row).length > 0) {
        stringifyEditField(state);
        fullfilEditRowSource(state);
      } else {
        fullfilEditRow(state);
      }
      resolve(response.data);
    }).catch(error => {
      utils.auth(error);
    });
  });
}

function loadRow(uri, state) {
  axios.post(constant.API_SERVER + uri).then(response => {
    state.row = response.data;
    if (!state.row) {
      state.row = {};
    }
    fullfilEditRowDynamic(state);
    fullfilEditRow(state);
    fullfilEditRowSource(state);
    fullfilSources(state);
    stringifyEditField(state);
    state.entryReady = true;
  }, response => {
    // error callback
  });
}

function loadEditOptionSource(state, option) {
  var arg = '';
  var bean = option;
  var cached = true;
  if ('object' == typeof option) {
    bean = option.bean;
    arg = option.arg;
    cached = undefined === option.cached ? true : option.cached;
  }

  if ( ! bean) {
    return;
  }
  var beanKey = option.dependency && arg ? (bean + ' ' + arg) : bean;
  if (cached && state.entry.sources[beanKey]) {
    return ;
  }

  if (cached && localStorage['options_' + beanKey] && ! constant.DEV_MODE) {
    Vue.set(state.entry.sources, beanKey, JSON.parse(localStorage['options_' + beanKey]));
    return;
  }


  var uri = "/api/entry/source?bean=" + bean;
  if (arg) {
    uri += "&arg=" + arg;
  }
  if (option.withParams) {
    uri += "&withParams=" + true;
  }

  axios.post(constant.API_SERVER + uri).then(response => {
      Vue.set(state.entry.sources, beanKey, response.data);
      if (cached) {
        localStorage['options_' + beanKey] = JSON.stringify(response.data);
      }
    }, error => {
      console.log(error);
    }
  );
}

export default {
  [types.PAGE_CLICKED] (state, bool) {
    state.pageClicked = bool;
  },
  [types.TOGGLE_MODAL] (state, link) {
    state.modal = ! state.modal;
    state.confirmCloseModal = false;
    state.blinkWarning = false;
    if (link) {
      state.modalLink = link;
    }
  },
  [types.CLOSE_MODAL] (state) {
    state.modal = false;
  },
  [types.SAVE_EDIT_ID] (state, id) {
    state.id = id;
    state.ids = null;
  },
  [types.CONFIRM_CLOSE_MODAL] (state) {
    state.confirmCloseModal = true;
  },
  [types.CANCEL_CLOSE_MODAL] (state) {
    state.confirmCloseModal = false;
  },
  [types.SAVE_EDIT_RESPONSE] (state, res) {
    state.response = res;
  },
  [types.SAVE_EDIT_HTML] (state, html) {
    state.entry = {};
    state.entry.panel = {};
    state.entry.panel.html = html;
  },
  [types.SAVE_EDIT_ENTRY] (state, entry) {
    state.entry = entry;
    fullfilEditRow(state);
    state.dataChanged = false;
  },
  [types.LOAD_EDIT_ENTRY](state, link) {
    loadEntry(state, link);
  },
  [types.LOAD_EDIT_ROW] (state, link) {
    state.row = {};
    var uri = link.uri;
    if (link.uri && link.uri.indexOf("?uri=") > 0) {
    } else {
      uri = '/api/consume/load?';
      if (link.mainUri) {
        uri += 'uri=' + untransform(link.mainUri);
      }
      if (link.entryUri) {
        if (link.mainUri) {
          uri += '&';
        }
        uri += 'entryUri=' + untransform(link.entryUri);
        if (link.action && link.action.client && link.action.client.class) {
          uri += '&class=' + link.action.client.class;
        }
      }

      if (link.get) {
        uri += '&f=' + link.get;
      } else if (link.commit) {
        uri += '&f=' + (link.commit.commit ? link.commit.commit : link.commit);
      }
      if (link.id) {
        uri += "&id=" + link.id;
      }
      if (link.q) {
        uri += "&q=" + link.q;
      }
    }
    state.entryReady = false;
    if (link.action) {
      loadEntry(state, link.action).then(res => {
        loadRow(uri, state);
      }, error => {
        console.log(error);
      });
    } else {
      loadRow(uri, state);
    }
  },
  [types.SAVE_EDIT_ROW](state, action) {
    var mainUri = untransform(action.uri);
    var data = {};
    var ignoreId = false;
    var dynamics = {};
    for (var i = 0; i < state.entry.panel.content.length; ++i) {
      var rows = state.entry.panel.content[i].column2 ?
        state.entry.panel.content[i].column.concat(state.entry.panel.content[i].column2) :
        state.entry.panel.content[i].column;
      for (var k = 0; k < rows.length; ++k) {
        var row = rows[k];
        ignoreId = fillRowValues(state, row, data, dynamics, ignoreId);
        if (row.more) {
          for (var j = 0; j < row.more.length; ++j) {
            ignoreId = fillRowValues(state, row.more[j], data, dynamics, ignoreId);
          }
        }
      }
    }

    for (var k in dynamics) {
      data[k] = JSON.stringify(dynamics[k]);
    }

    rowDotToObject(data);

    if (state.id && ! ignoreId) {
      data['id'] = state.row['id'];
    } else if (state.ids) {
      data['ids'] = state.ids;
    }

    var headers = {};
    // 3秒内不允许重复提交
    var currentTime = new Date().getTime();
    if ((currentTime - state.lastClickTime) / 1000 > 3) {
      headers["YG-UNIQUE-KEY"] = mainUri + currentTime;
      state.lastClickTime = currentTime;
    } else {
      headers["YG-UNIQUE-KEY"] = mainUri + state.lastClickTime;
    }

    var uri = '/api/consume/commit?uri=' + mainUri + '&f=' + action.client.commit;

    if (action.client.class) {
      uri += '&entryUri=' + state.entry.entryUri + '&class=' + action.client.class;
    }

    if (action.client.import) {
      uri = '/api/consume/importData?uri=' + mainUri;
      if (action.client.class) {
        uri += '&class=' + action.client.class + "&commit=" + action.client.commit;
      }
    } else if (action.client.setting) {
      uri = '/api/consume/setting?uri=' + mainUri;
    } else if (action.client.plusTab) {
      uri = action.client.uri + '?uri=' + mainUri;
      if (action.client.id) {
        uri += '&id=' + action.client.id;
      }
    }

    if (route.currentRoute.query.pid) {
      uri += '&pid=' + route.currentRoute.query.pid;
    }
    if (action.evict) {
      uri += '&evictName=' + action.evict.name;
    }

    axios({
      method: 'post',
      url: constant.API_SERVER + uri,
      data: data,
      headers: headers,
    }).then(response => {
        state.response = response.data;
        if (action.callafter) {
          (action.callafter)(state.response);
        }
        if (state.response.code == 500) {
          state.saved = '';
        } else {
          state.saved = state.saving;
        }
        state.saving = '';
        // ID如果修改，需要重置ID
        if (state.response.data && state.response.data.id) {
          state.row['id'] = state.response.data.id;
        }
        // 清除entry缓存
        utils.evictEntry(state.entry)
      }, error => {
        // error callback
        if (error.response && error.response.status == 401) {
          Vue.set(state.response, 'type', 'danger');
          Vue.set(state.response, 'msg', '会话超时, 请重新登录');
        }
        state.saving = '';
        state.saved = '';
      }
    );
  },
  [types.COMMIT_ERROR] (state, errMsg) {
    Vue.set(state.response, 'type', 'danger');
    Vue.set(state.response, 'msg', errMsg);
  },
  [types.EMPTY_EDIT_ROW] (state) {
    state.row = {};
  },
  [types.COMMIT_EDIT_ACTION] (state, data) {
    var commit = data.commit;
    var id = data.id;
    var mainUri = untransform(data.mainUri);
    var uri = '/api/consume/commit';
    if (data.uri) {
      uri = data.uri + '?id=' + id;
    } else {
      uri += '?uri=' + mainUri + '&f=' + commit + "&id=" + id;
    }

    if (data.type) {
      uri += '&type=' + data.type;
    }
    commitEditAction(state, uri, mainUri, data.callafter);
  },
  [types.LOAD_EDIT_OPTION_SOURCE] (state, option) {
    loadEditOptionSource(state, option);
  }
};
